<?php

class Crud_Controller_Layer extends Controller_Admin 
{
	/**
	 * @var AdminUserRow
	 */
	protected $_user;
	
	/**
	 * Specified table instance.
	 * @var Crud_Section
	 */
	protected $_section;
	
	/**
	 * Устанавливается в true после первого вызова preDispatch()
	 * @var boolean
	 */
	protected static $_preDispatchCalled = false;
	
	/**
	 * Specified row in table.
	 * @var Db_Row
	 */
	protected $_row;
	
	public function preDispatch()
	{
		if (self::$_preDispatchCalled) return;
		self::$_preDispatchCalled = true;
		
		parent::preDispatch();
		
		// Меняем storage для сессии, чтобы не было конфликтов с Zend_Auth на сайте.
		$auth = Zend_Auth::getInstance();
		$auth->setStorage(new Zend_Auth_Storage_Session('crud_auth'));
		
		// Добавляем путь до вьюшек, которые лежат в репозитории Crud
		$this->view->addScriptPath(realpath(dirname(__FILE__) . '/../views'));
		
		// Проверяем автозирован ли пользователь и есть ли у него права смотреть данную страницу
		$this->_checkAuth();
		$this->_checkPersmission();
		
		// Инициализация навигации. Сделано Брянском, поэтому так много букв и ничего не понятно
		$navigation = new Crud_Navigation();
		$navigation->init();
		
		$this->view->navigation($navigation);
		
		$role = $this->_getUser()->getAdminRoleRowByRoleId();
		$this->view->navigation()->setRole($role->getKey());
		
		$crumbs = $this->view->navigation()->breadcrumbs(); 
		$crumbs->setRenderInvisible(true);
		$this->view->title = strip_tags($crumbs);
		$this->view->acls = Crud_Acl::getInstance();
		$this->view->container = $navigation->mainPage;
		
		// Если не выбран раздел - прекращаем дальнейшую обработку
		if (!$this->_getParam('table')) {
			return ;
		}
		
		// Добавляем обработчики для раздела
		$section = $this->_getSection();
		Crud_Event::getInstance()->addHandlersBySection($section);
		
		// Делаем _forward на другой контроллер, если у раздела указан action_name
		if ($section->getActionName()) {
			$this->_forward($section->getActionName(), $section->getTableName());
		}
	}
	
	/**
	 * Отображение страницы "Действие запрещено".
	 * 
	 * @return void
	 */
	public function forbiddenAction()
	{
		$this->initView();
		$this->_disableView();
		echo $this->view->render('forbidden.phtml');
	}
	
	/**
	 * Отображение страницы "Запись заблокирована другим пользователем"
	 */
	public function lockedAction()
	{
		$this->_disableView();
		echo $this->view->render('locked.phtml');
	}
	
	/**
	 * Проверяет, авторизован ли пользователь. 
	 * Если не авторизован, запоминает текущий URI и перекидывает его на форму логина.
	 * 
	 * @return void
	 */
	protected function _checkAuth() 
	{
		if (!$this->_getUser()) {
			if (($this->getRequest()->getControllerKey() != 'login') && !$this->getRequest()->isXmlHttpRequest()) {
				$previousPage = $_SERVER['REQUEST_URI'];
				$session = new Zend_Session_Namespace('login');
				$session->previousPage = $previousPage;
			}
			
			$this->_goto(array('module' => 'admin', 'controller' => 'login', 'action' => 'index'), null, true);
		}
	}
	
	/**
	 * Проверяет права доступа к текущему действую через Acl.
	 * Если прав нет - делает редирект на forbiddenAction.
	 * Кроме этого добавляет во вьюшку информацию о правах доступа.
	 * Принимает необязательными параметрами ключ раздела и название действия.
	 * 
	 * @param string $section
	 * @param string $action
	 * @return void
	 */
	protected function _checkPersmission($section = null, $action = null) 
	{
		$user = $this->_getUser();
		$userRoleKey = $user->getAdminRoleRowByRoleId()->getKey();
		
		// Если раздел для проверки не передан в параметрах, пытаемся взять текущий
		if (!$section) {
			$section = $this->_getParam('table');
		}
		
		// Если действие с ресурсом не передано в параметрах, используем текущее.
		if (!$action) {
			$action = $this->getRequest()->getActionName();
		}
		
		// Если раздел отсутствует в acl, 
		// используем псевдоресурс dummy, для него установлены права 0000
		$acl = Crud_Acl::getInstance();
		$resources = $acl->getResources();
		if (!in_array($section, $resources)) {
			$section = 'dummy';
		}
		$this->view->acl = $aclArray = array(
			'read' => $acl->isAllowed($userRoleKey, $section, 'read'),
			'update' => $acl->isAllowed($userRoleKey, $section, 'update'),
			'create' => $acl->isAllowed($userRoleKey, $section, 'create'),
			'delete' => $acl->isAllowed($userRoleKey, $section, 'delete'),
		);
		
		// check read
		if ($action === 'list') {
			if (!$aclArray['read']) {
			    $this->_forward('forbidden');
			    return false;
			}
		// check update
		} elseif ($action === 'edit') {
			if (!$aclArray['update']) {
				$this->_forward('forbidden');
				return false;
			}
		// check create
		} elseif ($action === 'add') {
			if (!$aclArray['create']) {
				$this->_forward('forbidden');
				return false;
			}
		// check delete
		} elseif ($action === 'delete') {
			if (!$aclArray['delete']) {
				$this->_forward('forbidden');
				return false;
			}
		}
		
		return true;
	}

	/**
	 * Get Table object by table name in request.
	 *
	 * @return Crud_Section
	 */
	protected function _getSection()
	{
		if ($this->_section instanceof Crud_Section) {
			return $this->_section;
		}

		$table = $this->_getParam('table');
		if (!$table) {
			throw new Crud_Exception('Раздел не указан');
		}
		
		$row = AdminSectionPeer::getInstance()->fetchByKey($table);
		if (!$row) {
			throw new Exception("Раздел `$table` не существует");
		}
		
		$this->_section = $row;
		return $this->_section;
	}
	
	/**
	 * Getting row object by id in Request.
	 *
	 * @return Crud_Section_Row
	 */
	protected function _getRow()
	{
		if ($this->_row instanceof Crud_Section_Row) {
			return $this->_row;
		}
		
		$table = $this->_getSection();
		$id = $this->_getParam('id');
		if (!$id) {
			return $table->createRow();
		}

		$row = $table->getPeer()->find($id)->current();
		if (!$row) {
			throw new Exception('Невозможно получить запись из таблицы');
		}
		
		$row = new Crud_Section_Row($table, $row);
		$this->_row = $row;
		return $row;
	}
	
	/**
	 * Lazy loading user from db by auth identity.
	 * 
	 * @return AdminUserRow
	 */
	protected function _getUser()
	{
		if ($this->_user) {
			return $this->_user;
		}
		
		$login = Zend_Auth::getInstance()->getIdentity();
		if (!$login) {
			return false;
		}
		
		$peer = AdminUserPeer::getInstance();
		$user = $peer->findByLogin($login)->current();
		if (!$user instanceof AdminUserRow) {
			Zend_Auth::getInstance()->clearIdentity();
			return false;
		}
		
		$this->_user = $user;
		$this->view->currentUser = $user;
		return $this->_user;
	}
	
	/**
	 * Возвращает значение параметра из Request или Cookie.
	 * Значение в cookie привязывается к имени текущей таблицы.
	 * Формат cookie: "table_name:value,table2_name:value2"
	 * Если значение есть в Request - возвращается именно оно и добавляется в начало cookie.
	 * 
	 * @param string $name
	 * @param mixed $default optional
	 * @return mixed
	 */
	protected function _getCookieWrapParam($name, $default = null)
	{
		$param = $this->_getParam($name);
		$cookie = $this->getRequest()->getCookie($name);
		
		$table = $this->_getParam('table');
		$paramValueInCookie = null;
		if (preg_match("/$table:([^,]+)/", $cookie, $matches)) {
			$paramStringInCookie = $matches[0];
			$paramValueInCookie = $matches[1];
			
			if ($param) {
				$cookie = preg_replace("/$table:$paramValueInCookie,*/", "", $cookie);
			}
		}
		
		if ($param) {
			$cookie = "$table:$param,$cookie";
			$cookie = rtrim($cookie, ",");
			setcookie($name, $cookie, time()+60*60*24*30*12, '/');
			return $param;
		} else if ($paramValueInCookie != null) {
			return $paramValueInCookie;
		} else {
			return $default;
		}
	}
	
	/**
	 * Запускает все привязанные эвенты указанного типа.
	 * 
	 * @return boolean
	 */
	public function _runEvent($type, $options)
	{
		return Crud_Event::getInstance()->runHandlers($type, $options);
	}

}

